/*
 * Copyright (C) 2007  Vianney le Clément
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package fsahoraires.cours;

import java.util.*;

/**
 * Classe gérant les cours: la cache, la récupération de données et les
 * listeners de modification.
 */
public class CoursManager implements Iterable<Cours> {
	private Map<String, Cours> cache = new HashMap<String, Cours>();
	private List<CoursManagerListener> listeners = new ArrayList<CoursManagerListener>();

	private CoursFetcher fetcher = new CoursFetcher(this);

	public Cours getCours(String code) {
		return getCours(code, true);
	}

	public Cours getCours(String code, boolean fetch) {
		if (code == null)
			throw new NullPointerException("code ne peut pas être null");
		code = code.toUpperCase();
		Cours cours;
		boolean isNew = false;
		synchronized (cache) {
			cours = cache.get(code);
			if (cours == null) {
				cours = new Cours(code, this);
				cache.put(code, cours);
				isNew = true;
			}
		}
		if (isNew) {
			fireCoursAdded(cours);
			if (fetch)
				fetcher.add(cours);
		}
		return cours;
	}

	/**
	 * Renvoie un cours basé sur le cours donné (qui peut être disposé).
	 * 
	 * @param other
	 * @return
	 */
	public Cours getCours(Cours other) {
		synchronized(other) {
			if(!other.isDisposed() && other.getManager() == this)
				return other;
			Cours cours = getCours(other.getCode(), false);
			cours.setTitre(other.getTitre());
			cours.setAbreviation(other.getAbreviation());
			cours.setCredits(other.getCredits());
			cours.setHeures(other.getHeures());
			cours.setHoraire(1, other.getHoraire(1));
			cours.setHoraire(2, other.getHoraire(2));
			return cours;
		}
	}

	/**
	 * Dispose un cours (appelé uniquement par {@link Cours#dispose()})
	 * 
	 * @param cours
	 */
	void disposeCours(Cours cours) {
		synchronized (cache) {
			cache.remove(cours.getCode());
		}
		fireCoursDisposed(cours);
	}

	public SortedSet<Cours> getAllCours() {
		SortedSet<Cours> result;
		synchronized (cache) {
			result = new TreeSet<Cours>(cache.values());
		}
		return result;
	}

	public CoursFetcher getFetcher() {
		return fetcher;
	}

	void fireCoursAdded(Cours cours) {
		try {
			for (CoursManagerListener listener : listeners)
				listener.added(cours);
		} catch (ConcurrentModificationException e) {
			e.printStackTrace();
		}
	}

	void fireCoursBeforeModify(Cours cours) {
		try {
			for (CoursManagerListener listener : listeners)
				listener.beforeModify(cours);
		} catch (ConcurrentModificationException e) {
			e.printStackTrace();
		}
	}

	void fireCoursModified(Cours cours) {
		try {
			for (CoursManagerListener listener : listeners)
				listener.modified(cours);
		} catch (ConcurrentModificationException e) {
			e.printStackTrace();
		}
	}

	void fireCoursDisposed(Cours cours) {
		try {
			for (CoursManagerListener listener : listeners)
				listener.disposed(cours);
		} catch (ConcurrentModificationException e) {
			e.printStackTrace();
		}
	}

	public void addCoursManagerListener(CoursManagerListener listener) {
		listeners.add(listener);
	}

	public void removeCoursManagerListener(CoursManagerListener listener) {
		listeners.remove(listener);
	}

	public void close() {
		fetcher.interrupt();
	}

	protected void finalize() throws Throwable {
		close();
	}

	public Iterator<Cours> iterator() {
		return getAllCours().iterator();
	}

}
